/*
 * *
 *  * Created with IntelliJ RustRover.
 *  * Description:
 *  * Copyright Author: ZhenYi
 *  * DateTime: 2024/2/15 上午1:47
 *
 */

use rand::Rng;
use rustis::client::Client;
use rustis::resp::cmd;
use salvo::{Depot, handler, Request, Response};
use salvo::http::cookie::CookieBuilder;
use salvo::prelude::StatusCode;
use sea_orm::ActiveValue::Set;
use sea_orm::{ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter};
use sea_orm::prelude::Date;
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc::Sender;
use uuid::Uuid;
use crate::config::pgsql::userlist;
use crate::massage::{EmailOps, EmailQueue};
use crate::utensil::cookie_local::LocalCookie;

/// 注册输入
/// genre: 1 为管理员 2 为作者 3 为专家
/// username: 用户名
/// password: 密码
/// email: 邮箱
/// phone: 电话
/// name: 姓名
/// sex: 性别 1 为男 2 为女
/// birthday: 生日
///  -- 日期格式为 2024-02-15
/// address: 地址
/// -- 例如: 北京市海淀区中关村大街1号
/// postalcode: 邮编
///  -- 例如: 100000
/// jobtitle: 职称
///  -- 例如: 教授
/// companyname: 公司名
///  -- 例如: 北京大学
/// companyaddress: 公司地址
///  -- 例如: 北京市海淀区中关村大街1号
/// department_faculty: 部门或者学院
///  -- 例如: 计算机学院
/// research_direction: 研究方向
///  -- 例如: 人工智能
/// field_of_study: 研究领域
///  -- 例如: 计算机科学
#[derive(Deserialize,Serialize)]
pub struct RegInput{
    genre: i32,
    username: String,
    password: String,
    email: String,
    phone: String,
    name: String,
    sex: String,
    birthday: Date,
    address: String,
    postalcode: String,
    jobtitle: String,
    companyname: String,
    companyaddress: String,
    department_faculty: Option<String>,
    research_direction: String,
    field_of_study: Option<String>,
}


#[handler]
pub async fn register(req: &mut Request, depot: &mut Depot, res: &mut Response){
let input = match req.parse_json::<RegInput>().await{
        Ok(i) => i,
        Err(_) => {
            res.status_code(StatusCode::IM_USED).render("{\"Error\":\"Json parse error\"}");
            return;
        }
    };
    let db = depot.obtain::<DatabaseConnection>().unwrap();
    if input.genre == 1 {
        res.status_code(StatusCode::IM_USED).render("{\"Error\":\"User genre error\"}");
        return;
    }
    match userlist::Entity::find()
        .filter(userlist::Column::Username.contains(input.username.clone()))
        .one(db).await{
        Ok(ds) => {
            match ds {
                None => {}
                Some(e) => {
                    if e.username == input.username{
                        res.status_code(StatusCode::IM_USED).render("{\"Error\":\"Username already exists\"}");
                        return;
                    }
                }
            }
        }
        Err(_) => {}
    };
    let nums = userlist::Entity::find()
        .all(db)
        .await
        .unwrap()
        .len();
    let sex = input.sex.parse::<i32>().unwrap();
    let ns = userlist::ActiveModel{
        id: Set((nums + 1) as i32),
        genre: Set(input.genre),
        username: Set(input.username.clone()),
        password: Set(input.password.clone()),
        email: Set(input.email.clone()),
        phone: Set(input.phone.clone()),
        name: Set(input.name.clone()),
        sex: Set(sex),
        birthday: Set(input.birthday),
        address: Set(input.address.clone()),
        postalcode: Set(input.postalcode.clone()),
        jobtitle: Set(input.jobtitle.clone()),
        companyname:Set(input.companyname.clone()),
        companyaddress: Set(input.companyaddress.clone()),
        department_faculty:Set(input.department_faculty.clone()),
        research_direction: Set(input.research_direction.clone()),
        field_of_study: Set(input.field_of_study.clone()),
    };
    match ns.clone().insert(db).await{
        Ok(_nso) => {
            res.status_code(StatusCode::ACCEPTED).render(format!("{{\"OK\":\"Ok\"}}"));
            return;
        }
        Err(e) => {
            res.status_code(StatusCode::IM_USED).render(format!("{{\"Error\":\"{}\"}}",e));
            return;
        }
    }
}
#[derive(Deserialize,Serialize)]
pub struct CodeInput{
    email:String,
}
pub fn rand_code() ->String{
    let mut rng = rand::thread_rng();
    rng.gen_range(100000..999999).to_string()
}
#[handler]
pub async fn verification_code(req: &mut Request, depot: &mut Depot, res: &mut Response){
    let email = match req.parse_json::<CodeInput>().await{
        Ok(i) => i,
        Err(_) => {
            res.status_code(StatusCode::IM_USED).render("{\"Error\":\"Json parse error\"}");
            return;
        }
    };
    let cx = depot.obtain::<Sender<EmailQueue>>().unwrap();
    let client = depot.obtain::<Client>().unwrap();
    let code = rand_code();
    let cookie_v = rand_uuid_cookie();
    res.status_code(StatusCode::ACCEPTED).render("Ok");
    let cookie = CookieBuilder::new("ReG",cookie_v.clone());
    cx.send(
        EmailQueue{
            ops: EmailOps::VerificationCode,
            body: format!("{}",code),
            to: email.email.clone(),
        }
    ).await.unwrap();
    client.send(cmd("SELECT").arg("9"),None).await.unwrap();
    client.send(cmd("SET").arg(cookie_v.clone()).arg(code),None).await.unwrap();
    client.send(cmd("Expire").arg(cookie_v.clone()).arg(600),None).await.unwrap();
    res.add_cookie(cookie.build());

}

pub fn rand_uuid_cookie() ->String{
    let uuid = Uuid::new_v4();
    uuid.to_string()
}

#[derive(Deserialize,Serialize)]
pub struct VerCode{
    code:String
}
#[handler]
pub async fn verification_check(req: &mut Request, depot: &mut Depot, res: &mut Response) {
    let client = depot.obtain::<Client>().unwrap();
    let input = match req.parse_json::<VerCode>().await{
        Ok(i) => i,
        Err(_) => {
            res.status_code(StatusCode::IM_USED).render("{\"Error\":\"Json parse error\"}");
            return;
        }
    };
    let cookie = match req.cookie("ReG"){
        None => {
            res.status_code(StatusCode::IM_USED).render("{\"Error\":\"Cookie not found or v_code not find\"}");
            return;
        }
        Some(cookie) => cookie.value().to_string()
    };
    client.send(cmd("SELECT").arg("9"),None).await.unwrap();
    let code = match client.send(
        cmd("GET").arg(cookie.clone()),None
    ).await{
        Ok(code) => {
            code
        }
        Err(_) => {
            res.status_code(StatusCode::IM_USED).render("{\"Error\":\"v_code not found\"}");
            return;
        }
    };
    let input = format!("BulkString(\"{}\")",input.code);
    if input == code.to_string() {
        res.status_code(StatusCode::ACCEPTED).render("{\"OK\":\"OK\"}");
        res.add_cookie(
            CookieBuilder::new("ReG",cookie.clone())
                .expires(LocalCookie::future_60min())
                .path("/")
                .http_only(true)
                .build()
        );
        return;
    }else {
        res.status_code(StatusCode::IM_USED).render("{\"Error\":\"v_code error\"}");
        return;
    }
}